#include "stdafx.h"
#include "stdlib/timestamp.hpp"
#include "stdlib/exception.hpp"
#include <algorithm>
#include <time.h>
#include <Windows.h>
#if defined(JH_OS_LINUX)
	#include <unistd.h>
	#include <sys/time.h>
	#include <sys/times.h>
#endif

namespace JHCPP
{
	namespace stdlib
	{
		CTimeStamp::CTimeStamp()
		{
			update();
		}

		CTimeStamp::CTimeStamp(TimeVal tv)
		{
			m_ts = tv;
		}

		CTimeStamp::CTimeStamp(const CTimeStamp& other)
		{
			m_ts = other.m_ts;
		}

		CTimeStamp::~CTimeStamp()
		{
		}

		CTimeStamp& CTimeStamp::operator = (const CTimeStamp& other)
		{
			m_ts = other.m_ts;
			return *this;
		}

		CTimeStamp& CTimeStamp::operator = (TimeVal tv)
		{
			m_ts = tv;
			return *this;
		}

		void CTimeStamp::swap(CTimeStamp& timestamp)
		{
			std::swap(m_ts, timestamp.m_ts);
		}

		void CTimeStamp::update()
		{
		#if defined(JH_OS_WINDOWS)
			FILETIME ft;
			GetSystemTimeAsFileTime(&ft);

			ULARGE_INTEGER epoch; // UNIX epoch (1970-01-01 00:00:00) expressed in Windows NT FILETIME
			epoch.LowPart  = 0xD53E8000;
			epoch.HighPart = 0x019DB1DE;

			ULARGE_INTEGER ts;
			ts.LowPart  = ft.dwLowDateTime;
			ts.HighPart = ft.dwHighDateTime;
			ts.QuadPart -= epoch.QuadPart;
			m_ts = ts.QuadPart/10;

		#elif defined(JH_OS_LINUX)
			struct timeval tv;
			if (gettimeofday(&tv, NULL))
				throw jhException::SystemException("cannot get time of day");
			m_ts = TimeVal(tv.tv_sec)*resolution() + tv.tv_usec;
		#endif
		}

		bool CTimeStamp::operator == (const CTimeStamp& ts) const
		{
			return m_ts == ts.m_ts;
		}

		bool CTimeStamp::operator != (const CTimeStamp& ts) const
		{
			return m_ts != ts.m_ts;
		}

		bool CTimeStamp::operator >  (const CTimeStamp& ts) const
		{
			return m_ts > ts.m_ts;
		}

		bool CTimeStamp::operator >= (const CTimeStamp& ts) const
		{
			return m_ts >= ts.m_ts;
		}

		bool CTimeStamp::operator <  (const CTimeStamp& ts) const
		{
			return m_ts < ts.m_ts;
		}

		bool CTimeStamp::operator <= (const CTimeStamp& ts) const
		{
			return m_ts <= ts.m_ts;
		}

		CTimeStamp CTimeStamp::operator + (CTimeStamp::TimeDiff d) const
		{
			return CTimeStamp(m_ts + d);
		}

		CTimeStamp CTimeStamp::operator - (CTimeStamp::TimeDiff d) const
		{
			return CTimeStamp(m_ts - d);
		}

		CTimeStamp::TimeDiff CTimeStamp::operator - (const CTimeStamp& ts) const
		{
			return m_ts - ts.m_ts;
		}

		CTimeStamp& CTimeStamp::operator += (CTimeStamp::TimeDiff d)
		{
			m_ts += d;
			return *this;
		}

		CTimeStamp& CTimeStamp::operator -= (CTimeStamp::TimeDiff d)
		{
			m_ts -= d;
			return *this;
		}

		std::time_t CTimeStamp::epochTime() const
		{
			return std::time_t(m_ts/resolution());
		}

		CTimeStamp::UtcTimeVal CTimeStamp::utcTime() const
		{
			return m_ts*10 + (TimeDiff(0x01b21dd2) << 32) + 0x13814000;
		}

		CTimeStamp::TimeVal CTimeStamp::epochMicroseconds() const
		{
			return m_ts;
		}

		CTimeStamp::TimeDiff CTimeStamp::elapsed() const
		{
			CTimeStamp now;
			return now - *this;
		}

		bool CTimeStamp::isElapsed(CTimeStamp::TimeDiff interval) const
		{
			CTimeStamp now;
			CTimeStamp::TimeDiff diff = now - *this;
			return diff >= interval;
		}

		CTimeStamp CTimeStamp::fromEpochTime(std::time_t t)
		{
			return CTimeStamp(TimeVal(t)*resolution());
		}

		CTimeStamp CTimeStamp::fromUtcTime(UtcTimeVal val)
		{
			val -= (TimeDiff(0x01b21dd2) << 32) + 0x13814000;
			val /= 10;
			return CTimeStamp(val);
		}

		CTimeStamp::TimeVal CTimeStamp::resolution()
		{
			return 1000000;
		}

	#if defined(_WIN32)
		CTimeStamp CTimeStamp::fromFileTimeNP(UInt32 fileTimeLow, UInt32 fileTimeHigh)
		{
			ULARGE_INTEGER epoch; // UNIX epoch (1970-01-01 00:00:00) expressed in Windows NT FILETIME
			epoch.LowPart  = 0xD53E8000;
			epoch.HighPart = 0x019DB1DE;

			ULARGE_INTEGER ts;
			ts.LowPart  = fileTimeLow;
			ts.HighPart = fileTimeHigh;
			ts.QuadPart -= epoch.QuadPart;

			return CTimeStamp(ts.QuadPart/10);
		}

		void CTimeStamp::toFileTimeNP(UInt32& fileTimeLow, UInt32& fileTimeHigh) const
		{
			ULARGE_INTEGER epoch; // UNIX epoch (1970-01-01 00:00:00) expressed in Windows NT FILETIME
			epoch.LowPart  = 0xD53E8000;
			epoch.HighPart = 0x019DB1DE;

			ULARGE_INTEGER ts;
			ts.QuadPart  = m_ts*10;
			ts.QuadPart += epoch.QuadPart;
			fileTimeLow  = ts.LowPart;
			fileTimeHigh = ts.HighPart;
		}
	#endif

	}//end of namespace stdlib
}//end of namespace JHCPP
